package tazmania.entity;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;

import tazadum.engine.Entity;
import tazadum.engine.Interval;
import tazadum.engine.Signal;
import tazadum.engine.trigger.GreaterThanTrigger;
import tazadum.engine.trigger.Trigger;

public class SquarePusherEntity implements Entity {
	private static final int MAX_SPAWNS = 100;;
	private final Signal signal;
	private float screenRatio;

	private final Trigger spawnSquare = new GreaterThanTrigger(0.75f, 0);
	private final Trigger swipeSquare = new GreaterThanTrigger(0.75f, 0);

	private final int triggerChannel;
	private final int swipeChannel;

	private final float[][] colors = { { 1, 1, 1 }, { 1, 1, 1 }, { 1, 0, 0 } };
	private int colorIndex = 0;
	private final List<Spawn> spawns = new ArrayList<Spawn>(MAX_SPAWNS);
	private int squareList;

	public SquarePusherEntity(final Signal signal, final int triggerChannel, final int swipeChannel) {
		this.signal = signal;
		this.triggerChannel = triggerChannel;
		this.swipeChannel = swipeChannel;
	}

	@Override
	public void precalc(final GLAutoDrawable drawable, final float screenRatio) {
		final GL2 gl = drawable.getGL().getGL2();
		this.screenRatio = screenRatio;

		squareList = gl.glGenLists(1);

		gl.glNewList(squareList, GL2.GL_COMPILE);
		gl.glBegin(GL2.GL_QUADS);
		{
			final float S = 0.05f;
			final float R = 1;
			gl.glVertex2f(-R * S, -S);
			gl.glVertex2f(R * S, -S);
			gl.glVertex2f(R * S, S);
			gl.glVertex2f(-R * S, S);
		}
		gl.glEnd();
		gl.glEndList();
	}

	@Override
	public void update(final GLAutoDrawable drawable, final float globalTime, final float localTime, final float dt, final Interval interval) {
		final GL2 gl = drawable.getGL().getGL2();

		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glOrtho(-1, 1, -1 / screenRatio, 1 / screenRatio, 0.1f, 1000);
		gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glPushMatrix();
		gl.glLoadIdentity();
		gl.glTranslatef(0.0f, 0.0f, -4.0f);

		gl.glDisable(GL.GL_DEPTH_TEST);
		gl.glDisable(GL2.GL_LIGHTING);

		if (swipeSquare.trigger(signal.channel(swipeChannel))) {
			for (final Spawn spawn : spawns) {
				final float dy = spawn.dy;
				spawn.dy = -spawn.dx;
				spawn.dx = -dy;
				spawn.timeToLive += 1;
			}
		}

		if (spawnSquare.trigger(signal.channel(triggerChannel)) && spawns.size() < MAX_SPAWNS) {
			final float speed = 1.3f;
			final Spawn spawn = (colorIndex & 1) == 0 ? new Spawn(1, -.6f, 2.5f, -speed, 0) : new Spawn(-1, .6f, 2.5f, speed, 0);
			spawn.color = colors[colorIndex % colors.length];
			spawns.add(spawn);
			colorIndex++;
		}

		final ListIterator<Spawn> iterator = spawns.listIterator();
		while (iterator.hasNext()) {
			final Spawn spawn = iterator.next();
			gl.glPushMatrix();
			gl.glTranslatef(spawn.x, spawn.y, 0);
			// gl.glRotatef(localTime * 200f, 1, 1, 1);

			gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_FILL);
			gl.glColor4f(spawn.color[0], spawn.color[1], spawn.color[2], 1);
			gl.glCallList(squareList);
			gl.glPolygonMode(GL.GL_FRONT_AND_BACK, GL2.GL_LINE);
			gl.glColor4f(0, 0, 0, 1);
			gl.glCallList(squareList);

			spawn.x += spawn.dx * dt;
			spawn.y += spawn.dy * dt;
			spawn.timeToLive -= dt;

			if (spawn.timeToLive < 0) {
				iterator.remove();
			}

			gl.glPopMatrix();
		}

		// gl.glMatrixMode(GL2.GL_MODELVIEW);
		gl.glPopMatrix();
		gl.glMatrixMode(GL2.GL_PROJECTION);
		gl.glPopMatrix();
	}

	private static class Spawn {
		public float x;
		public float y;
		public float timeToLive;

		public float dx;
		public float dy;

		public float[] color;

		public Spawn(final float x, final float y, final float startTime, final float dx, final float dy) {
			this.x = x;
			this.y = y;
			this.dx = dx;
			this.dy = dy;
			this.timeToLive = startTime;
		}
	}
	
	@Override
	public void destroy(final GLAutoDrawable drawable) {
	}	
}
